{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "ec9a616c",
   "metadata": {},
   "source": [
    "# Robotics, Vision & Control 3e: for Python\n",
    "## Chapter 10: Light and Color\n",
    "\n",
    "Copyright (c) 2021- Peter Corke"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f09caefc",
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    import google.colab\n",
    "    print('Running on CoLab')\n",
    "    !pip install matplotlib\n",
    "    !pip install machinevision-toolbox-python\n",
    "    COLAB = True\n",
    "except:\n",
    "    COLAB = False\n",
    "\n",
    "from IPython.core.interactiveshell import InteractiveShell\n",
    "InteractiveShell.ast_node_interactivity = \"last_expr_or_assign\"\n",
    "\n",
    "import numpy as np\n",
    "%matplotlib widget\n",
    "import matplotlib.pyplot as plt\n",
    "import math\n",
    "from math import pi\n",
    "np.set_printoptions(\n",
    "    linewidth=120, formatter={\n",
    "        'float': lambda x: f\"{0:8.4g}\" if abs(x) < 1e-10 else f\"{x:8.4g}\"})\n",
    "np.random.seed(0)\n",
    "from machinevisiontoolbox.base import *\n",
    "from machinevisiontoolbox import Image\n",
    "from spatialmath.base import *"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a03d9f50",
   "metadata": {},
   "source": [
    "# 10.1 Spectral Representation of Light\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a3735064",
   "metadata": {},
   "outputs": [],
   "source": [
    "nm = 1e-9;\n",
    "lmbda = np.linspace(300, 1_000, 100) * nm;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6be9c94c",
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure()\n",
    "for T in np.arange(3_000, 6_001, 1_000):\n",
    "  plt.plot(lmbda / nm, blackbody(lmbda, T))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e75d9db0",
   "metadata": {},
   "outputs": [],
   "source": [
    "lamp = blackbody(lmbda, 2_600);\n",
    "sun = blackbody(lmbda, 5_778);\n",
    "plt.figure()\n",
    "plt.plot(lmbda / nm, np.c_[lamp / np.max(lamp), sun / np.max(sun)]);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7e7faca7",
   "metadata": {},
   "source": [
    "## 10.1.1 Absorption\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b9038eaa",
   "metadata": {},
   "outputs": [],
   "source": [
    "sun_ground = loadspectrum(lmbda, \"solar\");\n",
    "plt.figure()\n",
    "plt.plot(lmbda / nm, sun_ground);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7735497b",
   "metadata": {},
   "outputs": [],
   "source": [
    "lmbda = np.linspace(300, 1_000, 100) * nm;\n",
    "A = loadspectrum(lmbda, \"water\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "89802a89",
   "metadata": {},
   "outputs": [],
   "source": [
    "d = 5;\n",
    "T = 10 ** (-A * d);\n",
    "plt.figure()\n",
    "plt.plot(lmbda / nm, T);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "606d1441",
   "metadata": {},
   "source": [
    "## 10.1.2 Reflectance\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e1ff0f57",
   "metadata": {},
   "outputs": [],
   "source": [
    "lmbda = np.linspace(100, 10_000, 100) * nm;\n",
    "R = loadspectrum(lmbda, \"redbrick\");\n",
    "plt.figure()\n",
    "plt.plot(lmbda / (1_000 * nm), R);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "47356a4d",
   "metadata": {},
   "source": [
    "## 10.1.3 Luminance\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "13746f93",
   "metadata": {},
   "outputs": [],
   "source": [
    "lmbda = np.arange(400, 701) * nm;\n",
    "E = loadspectrum(lmbda, \"solar\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "69607261",
   "metadata": {},
   "outputs": [],
   "source": [
    "R = loadspectrum(lmbda, \"redbrick\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d0707f3b",
   "metadata": {},
   "outputs": [],
   "source": [
    "L = E * R;\n",
    "plt.figure()\n",
    "plt.plot(lmbda / nm, L);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "89c37467",
   "metadata": {},
   "source": [
    "# 10.2 Color\n",
    "## 10.2.1 The Human Eye\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "487c48d8",
   "metadata": {},
   "outputs": [],
   "source": [
    "cones = loadspectrum(lmbda, \"cones\");\n",
    "plt.figure()\n",
    "plt.plot(lmbda / nm, cones);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "48a657ce",
   "metadata": {},
   "source": [
    "### 10.2.1.1 Perceived Brightness\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "536527c6",
   "metadata": {},
   "outputs": [],
   "source": [
    "human = luminos(lmbda);\n",
    "plt.figure()\n",
    "plt.plot(lmbda / nm,  human);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "03548df7",
   "metadata": {},
   "outputs": [],
   "source": [
    "luminos(450 * nm) / luminos(550 * nm)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "803ad1bd",
   "metadata": {},
   "source": [
    "## 10.2.2 Camera sensor\n",
    "## 10.2.3 Measuring Color\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c8c97a4c",
   "metadata": {},
   "outputs": [],
   "source": [
    "np.sum(np.c_[L, L, L] * cones * nm, axis=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "58f57cb3",
   "metadata": {},
   "source": [
    "## 10.2.4 Reproducing Colors\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5b6a578a",
   "metadata": {},
   "outputs": [],
   "source": [
    "cmf = cmfrgb(lmbda);\n",
    "plt.figure()\n",
    "plt.plot(lmbda / nm, cmf);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b9c78c6e",
   "metadata": {},
   "outputs": [],
   "source": [
    "green = cmfrgb(500 * nm)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d32a9b74",
   "metadata": {},
   "outputs": [],
   "source": [
    "w = -np.min(green)\n",
    "feasible_green = green + w"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3894eb1d",
   "metadata": {},
   "outputs": [],
   "source": [
    "RGB_brick = cmfrgb(lmbda, L)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7713b244",
   "metadata": {},
   "source": [
    "## 10.2.5 Chromaticity Coordinates\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a99058c0",
   "metadata": {},
   "outputs": [],
   "source": [
    "rg = lambda2rg(np.linspace(400, 700, 100) * nm);\n",
    "plt.figure()\n",
    "plt.plot(rg[:, 0], rg[:, 1]);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2895658c",
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure()\n",
    "plot_spectral_locus(\"rg\");\n",
    "primaries = lambda2rg(cie_primaries()).T\n",
    "plot_point(primaries, \"o\");\n",
    "green_cc = lambda2rg(500 * nm)\n",
    "plot_point(green_cc, \"gx\");\n",
    "white_cc = tristim2cc([1, 1, 1])\n",
    "plot_point(white_cc, \"o\");\n",
    "\n",
    "plt.plot((green_cc[0], white_cc[0]), (green_cc[1], white_cc[1]), \"g\")  # not in book\n",
    "plot_polygon(primaries, close=True, filled=True, facecolor='yellow', edgecolor='none', alpha=0.75)  # not in book\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c8523973",
   "metadata": {},
   "outputs": [],
   "source": [
    "cmf = cmfxyz(lmbda);\n",
    "plt.figure()\n",
    "plt.plot(lmbda / nm, cmf);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "761331f0",
   "metadata": {},
   "outputs": [],
   "source": [
    "xy = lambda2xy(lmbda);\n",
    "plt.figure()\n",
    "plt.plot(xy[:,0], xy[:,1], \"k\");\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f17e457c",
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure()\n",
    "plot_chromaticity_diagram(\"xy\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1a1e178a",
   "metadata": {},
   "outputs": [],
   "source": [
    "lambda2xy(550 * nm)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3028323a",
   "metadata": {},
   "outputs": [],
   "source": [
    "lamp = blackbody(lmbda, 2_600);\n",
    "lambda2xy(lmbda, lamp)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ef2435d4",
   "metadata": {},
   "source": [
    "## 10.2.6 Color Names\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dc8210ba",
   "metadata": {},
   "outputs": [],
   "source": [
    "name2color(\"orange\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "472a94c7",
   "metadata": {},
   "outputs": [],
   "source": [
    "bs = name2color(\"orange\", \"xy\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "47b436b3",
   "metadata": {},
   "outputs": [],
   "source": [
    "name2color(\".*coral.*\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3e2ccc04",
   "metadata": {},
   "outputs": [],
   "source": [
    "color2name([0.45, 0.48], \"xy\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ecc6493e",
   "metadata": {},
   "source": [
    "## 10.2.7 Other Color and Chromaticity Spaces\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a7b421bf",
   "metadata": {},
   "outputs": [],
   "source": [
    "colorspace_convert([1, 0, 0], \"RGB\", \"HSV\")\n",
    "colorspace_convert([0, 1, 0], \"RGB\", \"HSV\")\n",
    "colorspace_convert([0, 0, 1], \"RGB\", \"HSV\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1bdaba58",
   "metadata": {},
   "outputs": [],
   "source": [
    "colorspace_convert([0, 0.5, 0], \"RGB\", \"HSV\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "963fe26e",
   "metadata": {},
   "outputs": [],
   "source": [
    "colorspace_convert([0.4, 0.4, 0.4], \"RGB\", \"HSV\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "85640ce8",
   "metadata": {},
   "outputs": [],
   "source": [
    "colorspace_convert(np.array([0, 0.5, 0]) + np.array([0.4, 0.4, 0.4]), \"RGB\", \"HSV\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dbfea31b",
   "metadata": {},
   "outputs": [],
   "source": [
    "flowers = Image.Read(\"flowers4.png\")\n",
    "flowers.disp()\n",
    "flowers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6dca9ea2",
   "metadata": {},
   "outputs": [],
   "source": [
    "hsv = flowers.colorspace(\"HSV\")\n",
    "hsv"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "18e328e5",
   "metadata": {},
   "outputs": [],
   "source": [
    "hsv.plane(\"H\").disp();\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "af91cc40",
   "metadata": {},
   "outputs": [],
   "source": [
    "hsv.plane(\"S\").disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6315e8a2",
   "metadata": {},
   "outputs": [],
   "source": [
    "hsv.plane(\"V\").disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3bc5ee72",
   "metadata": {},
   "outputs": [],
   "source": [
    "Lab = flowers.colorspace(\"L*a*b*\")\n",
    "Lab"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3d2cb848",
   "metadata": {},
   "outputs": [],
   "source": [
    "Lab.plane(\"a*:\").disp();\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "15f710bc",
   "metadata": {},
   "outputs": [],
   "source": [
    "Lab.plane(\"b*:\").disp();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d849bd4d",
   "metadata": {},
   "source": [
    "## 10.2.8 Transforming between Different Primaries\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dc15ebbd",
   "metadata": {},
   "outputs": [],
   "source": [
    "C = np.array([\n",
    "    [0.7347,  0.2738, 0.1666],\n",
    "    [0.2653,  0.7174, 0.0088],\n",
    "    [0,       0.0089, 0.8245]]);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8c316003",
   "metadata": {},
   "outputs": [],
   "source": [
    "white = np.array([0.3127, 0.3290, 0.3582]);\n",
    "J = np.linalg.inv(C) @  white / white[1]\n",
    "C @ np.diag(J)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "9d0eaeed",
   "metadata": {},
   "outputs": [],
   "source": [
    "XYZ_brick = (C @ np.diag(J) @ RGB_brick).T"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7d3beda1",
   "metadata": {},
   "outputs": [],
   "source": [
    "tri = tristim2cc(XYZ_brick)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c291ac58",
   "metadata": {},
   "outputs": [],
   "source": [
    "color2name(tri, \"xy\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e5a25e1f",
   "metadata": {},
   "outputs": [],
   "source": [
    "## 10.2.9 What Is White?\n",
    "d65 = blackbody(lmbda, 6_500);\n",
    "lambda2xy(lmbda, d65)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b413f416",
   "metadata": {},
   "outputs": [],
   "source": [
    "ee = np.ones(lmbda.shape);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "373226ac",
   "metadata": {},
   "outputs": [],
   "source": [
    "lambda2xy(lmbda, ee)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8004058f",
   "metadata": {},
   "source": [
    "# 10.3 Advanced Topics\n",
    "## 10.3.2 Color Constancy\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ff173ab0",
   "metadata": {},
   "outputs": [],
   "source": [
    "lmbda = np.arange(400, 701) * nm;\n",
    "R = loadspectrum(lmbda, \"redbrick\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4bc490b3",
   "metadata": {},
   "outputs": [],
   "source": [
    "sun = loadspectrum(lmbda, \"solar\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "97b4a852",
   "metadata": {},
   "outputs": [],
   "source": [
    "lamp = blackbody(lmbda, 2_600);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "97a86ea8",
   "metadata": {},
   "outputs": [],
   "source": [
    "xy_sun = lambda2xy(lmbda, sun * R)\n",
    "xy_lamp = lambda2xy(lmbda, lamp * R)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b025fd51",
   "metadata": {},
   "source": [
    "## 10.3.4 Color Change Due to Absorption\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8ce6bb1d",
   "metadata": {},
   "outputs": [],
   "source": [
    "lmbda = np.arange(400, 701) * nm;\n",
    "R = loadspectrum(lmbda, \"redbrick\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "789e7791",
   "metadata": {},
   "outputs": [],
   "source": [
    "sun = loadspectrum(lmbda, \"solar\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7a6ed73b",
   "metadata": {},
   "outputs": [],
   "source": [
    "A = loadspectrum(lmbda, \"water\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ca32e88f",
   "metadata": {},
   "outputs": [],
   "source": [
    "d = 2;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "512f0757",
   "metadata": {},
   "outputs": [],
   "source": [
    "T = 10.0 ** (-d * A);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "adad189f",
   "metadata": {},
   "outputs": [],
   "source": [
    "L = sun * R * T;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "80591dd4",
   "metadata": {},
   "outputs": [],
   "source": [
    "xy_water = lambda2xy(lmbda, L)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fff18330",
   "metadata": {},
   "source": [
    "## 10.3.6 Gamma\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8e3633a2",
   "metadata": {},
   "outputs": [],
   "source": [
    "wedge = np.linspace(0, 1, 11).reshape(1,-1);\n",
    "wedge"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a41f67f4",
   "metadata": {},
   "outputs": [],
   "source": [
    "Image(wedge).disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "80b7d6e7",
   "metadata": {},
   "outputs": [],
   "source": [
    "Image(wedge ** (1 / 2.2)).disp();"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8b5fe6ee",
   "metadata": {},
   "source": [
    "# 10.4 Application: Color Images\n",
    "## 10.4.1 Comparing Color Spaces\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5af36e1a",
   "metadata": {},
   "outputs": [],
   "source": [
    "lmbda = np.linspace(400, 700, 100) * nm;\n",
    "macbeth = loadspectrum(lmbda, \"macbeth\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "50d81b76",
   "metadata": {},
   "outputs": [],
   "source": [
    "d65 = loadspectrum(lmbda, \"D65\") * 3e9;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "72cb8b04",
   "metadata": {},
   "outputs": [],
   "source": [
    "XYZ = np.empty((18, 3));\n",
    "Lab = np.empty((18, 3));\n",
    "for i in range(18):\n",
    "  L = macbeth[:,i] * d65;\n",
    "  tristim = np.maximum(cmfrgb(lmbda, L), 0);\n",
    "  RGB = tristim.reshape((1, 1, 3)).astype(np.float32);\n",
    "  XYZ[i,:] = colorspace_convert(RGB, \"rgb\", \"xyz\");\n",
    "  Lab[i,:] = colorspace_convert(RGB, \"rgb\", \"L*a*b*\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6880c0a4",
   "metadata": {},
   "outputs": [],
   "source": [
    "xy = tristim2cc(XYZ);\n",
    "ab = Lab[:, 1:];\n",
    "xy.shape, ab.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "281d5ddd",
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.figure()\n",
    "plot_chromaticity_diagram(\"xy\");"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "42615c62",
   "metadata": {},
   "source": [
    "## 10.4.2 Shadow Removal\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "927dc21a",
   "metadata": {},
   "outputs": [],
   "source": [
    "im = Image.Read(\"parks.png\", dtype=\"float\")\n",
    "im.disp();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0206cdda",
   "metadata": {},
   "outputs": [],
   "source": [
    "im = Image.Read(\"parks.png\", gamma=\"sRGB\", dtype=\"float\")\n",
    "s = shadow_invariant(im.image, 0.7);\n",
    "Image(s).disp(interpolation=\"none\", badcolor=\"red\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2b61352f",
   "metadata": {
    "lines_to_next_cell": 2
   },
   "outputs": [],
   "source": [
    "if not COLAB:\n",
    "    theta = esttheta(im)  # interactive graphics, cannot work with CoLab"
   ]
  }
 ],
 "metadata": {
  "jupytext": {
   "cell_metadata_filter": "-all",
   "main_language": "python",
   "notebook_metadata_filter": "-all"
  },
  "kernelspec": {
   "display_name": "dev",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.9"
  },
  "vscode": {
   "interpreter": {
    "hash": "b7d6b0d76025b9176285a6442c3dd6dd39bcfe7241029b7898b7106bd5e9b472"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
